﻿using System;
using System.Drawing;

using Storm.TextEditor.Editor;
using Storm.TextEditor.Editor.Text;

namespace Storm.TextEditor.Parser.Objects
{
    /// <summary>
    /// Represents a range of rows that are formatted.
    /// </summary>
    public class FormatRange
    {
        #region Fields

        private Color backColor = Color.Empty;
        private Color foreColor = Color.Empty;
        private Color waveColor = Color.Empty;

        private object tag = null;

        private TextRange bounds = new TextRange();
        private TextRange oldBounds = new TextRange();
        private SyntaxDocument document = null;

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the background color of the FormatRange.
        /// </summary>
        public Color BackColor
        {
            get { return backColor; }
            set
            {
                backColor = value;
                this.Apply();
            }
        }

        /// <summary>
        /// Gets or sets the foreground color of the FormatRange.
        /// </summary>
        public Color ForeColor
        {
            get { return foreColor; }
            set
            {
                foreColor = value;
                this.Apply();
            }
        }

        /// <summary>
        /// Gets or sets the color of waves in the FormatRange.
        /// </summary>
        public Color WaveColor
        {
            get { return waveColor; }
            set
            {
                waveColor = value;
                this.Apply();
            }
        }

        /// <summary>
        /// Gets or sets the bounds of the FormatRange.
        /// </summary>
        public TextRange Bounds
        {
            get { return bounds; }
            set { bounds = value; }
        }

        /// <summary>
        /// Gets or sets the tag of the FormatRange.
        /// </summary>
        public object Tag
        {
            get { return tag; }
            set { tag = value; }
        }

        /// <summary>
        /// Gets or sets the document of the FormatRange.
        /// </summary>
        internal SyntaxDocument Document
        {
            get { return document; }
            set { document = value; }
        }

        #endregion

        #region Methods

        #region Public

        /// <summary>
        /// Returns whether the FormatRange's bounds contains the given TextPoint as an int.
        /// </summary>
        /// <param name="tp">TextPoint to check whether is contained in the FormatRange.</param>
        /// <returns>Whether the FormatRange's bounds contains the given TextPoint as an int.</returns>
        public int Contains(TextPoint tp)
        {
            return Contains(tp.X, tp.Y);
        }

        /// <summary>
        /// Returns whether the FormatRange's bounds contains the given coordinates as an int.
        /// </summary>
        /// <param name="x">X coordinate to check whether is contained in the FormatRange.</param>
        /// <param name="y">Y coordinate to check whether is contained in the FormatRange.</param>
        /// <returns>Whether the FormatRange's bounds contains the given coordinates as an int.</returns>
        public int Contains(int x, int y)
        {
            if (y < Bounds.FirstRow)
                return -1;

            if (y > Bounds.LastRow)
                return 1;

            if (y == Bounds.FirstRow && x < Bounds.FirstColumn)
                return -1;

            if (y == Bounds.LastRow && x > Bounds.LastColumn)
                return 1;

            return 0;
        }

        /// <summary>
        /// Tells the document of the FormatRange to reparse the row contained in the FormatRange.
        /// </summary>
        public void Apply()
        {
            if (this.Document == null)
                return;

            this.ApplyOld();
            for (int i = this.Bounds.FirstRow; i <= this.Bounds.LastRow; i++)
            {
                if (i < 0 || i >= this.Document.Count)
                    return;

                Row row = this.Document[i];

                if (row == null)
                    return;

                if (row.RowState == RowState.AllParsed)
                    row.AddToParseQueue();
            }
        }

        /// <summary>
        /// Tells the document of the FormatRange to reparse the old rows contained in the FOrmatRange.
        /// </summary>
        public void ApplyOld()
        {
            for (int i = oldBounds.FirstRow; i <= oldBounds.LastRow; i++)
            {
                if (i < 0 || i >= Document.Count)
                    return;

                Row row = this.Document[i];

                if (row == null)
                    return;

                if (row.RowState == RowState.AllParsed)
                    row.AddToParseQueue();
            }
        }

        #endregion

        #region Protected

        /// <summary>
        /// Updates the bounds according to the new given bounds.
        /// </summary>
        /// <param name="s">Sender object.</param>
        /// <param name="e">EventArgs.</param>
        protected virtual void BoundsChanged(object s, EventArgs e)
        {
            // Apply the new bounds.
            this.Apply();

            // Update the old bounds.
            oldBounds.FirstColumn = bounds.FirstColumn;
            oldBounds.LastColumn = bounds.LastColumn;
            oldBounds.FirstRow = bounds.FirstRow;
            oldBounds.LastRow = bounds.LastRow;
        }

        #endregion

        #endregion

        /// <summary>
        /// Initializes a new instance of FormatRange.
        /// </summary>
        public FormatRange()
        {
            this.Bounds = new TextRange();
            this.Bounds.Change += new EventHandler(BoundsChanged);
        }

        /// <summary>
        /// Initializes a new instance of FormatRange.
        /// </summary>
        public FormatRange(TextRange bounds, Color waveColor)
        {
            this.WaveColor = waveColor;

            this.Bounds = bounds;
            this.Bounds.Change += new EventHandler(BoundsChanged);
        }

        /// <summary>
        /// Initializes a new instance of FormatRange.
        /// </summary>
        public FormatRange(TextRange bounds, Color foreColor, Color backColor)
        {
            this.BackColor = backColor;
            this.ForeColor = foreColor;

            this.Bounds = bounds;
            this.Bounds.Change += new EventHandler(BoundsChanged);
        }

        /// <summary>
        /// Initializes a new instance of FormatRange.
        /// </summary>
        public FormatRange(TextRange bounds, Color foreColor, Color backColor, Color waveColor)
        {
            this.BackColor = backColor;
            this.ForeColor = foreColor;
            this.WaveColor = waveColor;

            this.Bounds = bounds;
            this.Bounds.Change += new EventHandler(BoundsChanged);
        }
    }
}
